﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Linq;
using ScrumTable.BL.DM.DataManagement;
using ScrumTable.Common.Collections;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ScrumTable.Common.Test;
using ScrumTable.DL.Data.Generic.Cache;
using ScrumTable.DL.Data.Generic.Test;
using ScrumTable.DL.Data.Local.Test;
using ScrumTable.DL.Data.Schema.ScrumTableV1;

#endregion

namespace ScrumTable.BL.DM.Test.DataManagement
{
    /// <summary>
    /// This is the test class for Bug class.
    /// </summary>
    [TestClass]
    public class BugTest : ScrumItemTest<Bug, BugSchema>
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        /// <summary>
        /// Gets the default instance to test.
        /// </summary>
        protected override Bug ToTest
        {
            get { return Context.Projects[Resources.Project1Id].Stories[Resources.UserStory4Id].Bugs[Resources.Bug3Id]; }
        }

        private ScrumState ActiveState { get { return ToTest.ItemType.StateMappings[Resources.BugActiveState]; } }
        private ScrumState ClosedState { get { return ToTest.ItemType.StateMappings[Resources.BugClosedState]; } }
        private ScrumState ResolvedState { get { return ToTest.ItemType.StateMappings[Resources.BugResolvedState]; } }

        #endregion

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new BugTest instance.
        /// </summary>
        public BugTest()
        {
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Use TestInitialize to run code before running each test .
        /// </summary>
        [TestInitialize]
        public override void TestInitialize()
        {
            base.TestInitialize();
        }

        /// <summary>
        /// Use TestCleanup to run code after each test has run.
        /// </summary>
        [TestCleanup]
        public override void TestCleanup()
        {
            base.TestCleanup();
        }

        [TestMethod]
        public void TestValueChangeOnDL()
        {
            TestValueChangeWithEventOnDL(DataObjectSchema.MemberId, Resources.FullMember2IdChain, "AssignedMember");
            TestValueChangeWithEventOnDL(DataObjectSchema.Description, "[New Description]");
            TestValueChangeWithEventOnDL(DataObjectSchema.Name, "[New Name]");
            TestValueChangeWithEventOnDL(DataObjectSchema.Ranking, 5.6);
            TestValueChangeWithEventOnDL(DataObjectSchema.State, ResolvedState.Name);

            TestValueChangeWithEventOnDL(
                DataObjectSchema[TestFieldDefinitionSet.SeverityFieldName],
                ToTest.ItemType.FieldMeta.TryGetByName(TestFieldDefinitionSet.SeverityFieldName).AllowedValues.ToEnumerable().Second());
            TestValueChangeWithEventOnDL(
                DataObjectSchema[TestFieldDefinitionSet.PriorityFieldName],
                3);
        }

        [TestMethod]
        public void TestValueChangeOnDM()
        {
            TestValueChangeWithEventOnDM("Ranking", -10.5);
            TestValueChangeWithEventOnDM("Ranking", 0.0);
            TestValueChangeWithEventOnDM("Ranking", 5.6);
            TestValueChangeWithEventOnDM("Ranking", null);
            TestValueChangeWithEventOnDM("State", ResolvedState);
        }

        [TestMethod]
        public void TestParentEvent()
        {
            Bug testObject = ToTest;
            Iteration newParent1 = Context.Projects[Resources.Project1Id].Iterations[Resources.Iteration1Id];
            Project newParent2 = Context.Projects[Resources.Project1Id];

            testObject.FireParentChangedEvent = false;
            TestAssertParentChangedFired(testObject, newParent1.Bugs, false);

            testObject.FireParentChangedEvent = true;
            TestAssertParentChangedFired(testObject, newParent2.Bugs, true);
        }

        [TestMethod]
        public override void TestParent()
        {
            Assert.IsNotNull(ToTest.Parent);
        }

        [TestMethod]
        public void TestSeverityMeta()
        {
            Assert.IsNotNull(ToTest.ItemType.FieldMeta.TryGetByName(TestFieldDefinitionSet.SeverityFieldName));
            Assert.IsNotNull(ToTest.ItemType.FieldMeta.TryGetByName(TestFieldDefinitionSet.SeverityFieldName).AllowedValues);
            Assert.IsTrue(ToTest.ItemType.FieldMeta.TryGetByName(TestFieldDefinitionSet.SeverityFieldName).AllowedValues.ToEnumerable().Count() > 0);
        }

        [TestMethod]
        public void TestSeverityGet()
        {
            Assert.IsFalse(string.IsNullOrEmpty(ToTest[DataObjectSchema[TestFieldDefinitionSet.SeverityFieldName]] as string));
        }

        [TestMethod]
        public void TestSeveritySet()
        {
            string newSeverity = ToTest.ItemType.FieldMeta.TryGetByName(TestFieldDefinitionSet.SeverityFieldName).AllowedValues.ToEnumerable().Last().ToString();
            Assert.AreNotEqual(newSeverity, DataObject[DataObjectSchema[TestFieldDefinitionSet.SeverityFieldName]]);
            DataObject[DataObjectSchema[TestFieldDefinitionSet.SeverityFieldName]] = newSeverity;
            Assert.AreEqual(newSeverity, DataObject[DataObjectSchema[TestFieldDefinitionSet.SeverityFieldName]]);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void TestInvalidSeveritySet()
        {
            string newSeverity = "This IS an invalid value...";
            Assert.AreNotEqual(newSeverity, DataObject[DataObjectSchema[TestFieldDefinitionSet.SeverityFieldName]]);
            DataObject[DataObjectSchema[TestFieldDefinitionSet.SeverityFieldName]] = newSeverity;
        }

        [TestMethod]
        public void TestPriorityGet()
        {
            Assert.AreEqual(5, DataObject[DataObjectSchema[TestFieldDefinitionSet.PriorityFieldName]]);
        }

        [TestMethod]
        public void TestPrioritySet()
        {
            int? newPriority = 5;
            DataObject[DataObjectSchema[TestFieldDefinitionSet.PriorityFieldName]] = newPriority;
            Assert.AreEqual(newPriority, DataObject[DataObjectSchema[TestFieldDefinitionSet.PriorityFieldName]]);
        }

        [TestMethod]
        public void TestStateGet()
        {
            Assert.AreEqual(DataObject[DataObjectSchema.State], ToTest.State.ToString());
        }

        [TestMethod]
        public void TestStateSet()
        {
            ToTest.State = ResolvedState;
            Assert.AreEqual(ResolvedState.ToString(), DataObject[DataObjectSchema.State]);
        }

        [TestMethod]
        public void TestStateSetWithUndoManager()
        {
            Assert.IsFalse(Context.UndoManager.CanUndo);
            ToTest.State = ResolvedState;
            Assert.IsFalse(Context.UndoManager.CanUndo);
        }

        [TestMethod]
        public void TestStateIsReady()
        {
            Assert.IsTrue(ToTest.ItemType.HasStrictWorkflow);
            Assert.IsTrue(ToTest.StateIsReady);

            ToTest.State = ResolvedState;
            Assert.IsFalse(ToTest.StateIsReady);
            Assert.IsFalse(ToTest.CanChangeState(ClosedState));

            NotifyPropertyTestStub<Bug> eventTester = new NotifyPropertyTestStub<Bug>(ToTest);
            eventTester.PropertyName = "StateIsReady";

            using (eventTester.Register())
            {
                AcceptChangesOnDataLayer();
                Assert.AreEqual(1, eventTester.PropertyFiredCount);
                Assert.AreEqual(1, eventTester.PropertyFiringCount);
            }

            Assert.IsTrue(ToTest.StateIsReady);
            Assert.IsTrue(ToTest.CanChangeState(ClosedState));
        }

        [TestMethod]
        public void TestStateIsReadyWithReject()
        {
            ToTest.State = ResolvedState;

            NotifyPropertyTestStub<Bug> eventTester = new NotifyPropertyTestStub<Bug>(ToTest);
            eventTester.PropertyName = "StateIsReady";

            using (eventTester.Register())
            {
                RejectChangesOnDataLayer();
                Assert.AreEqual(0, eventTester.PropertyFiredCount);
                Assert.AreEqual(0, eventTester.PropertyFiringCount);
            }

            Assert.IsFalse(ToTest.StateIsReady);
            Assert.IsFalse(ToTest.CanChangeState(ClosedState));
        }

        [TestMethod]
        [ExpectedException(typeof(TransitionNotAllowedException))]
        public void TestStateSetWithException()
        {
            CheckStateSet(ActiveState, ClosedState); // Active -> Closed; transation is not allowed 
        }

        [TestMethod]
        public void TestMemberIdGet()
        {
            Assert.AreEqual(DataObject[DataObjectSchema.MemberId], ToTest.AssignedMember.IdChain);
        }

        [TestMethod]
        public void TestMemberIdSet()
        {
            Member newMember = ToTest.Context.Projects[Resources.Project1Id].Members[Resources.Member2Id];
            ToTest.AssignedMember = newMember;
            Assert.AreEqual(newMember.IdChain, DataObject[DataObjectSchema.MemberId]);
        }

        [TestMethod]
        public void TestRankingGet()
        {
            Assert.AreEqual(DataObject[DataObjectSchema.Ranking], ToTest.Ranking);
        }

        [TestMethod]
        public void TestRankingSet()
        {
            double? newStackRank = 105;
            ToTest.Ranking = newStackRank;
            Assert.AreEqual(newStackRank, DataObject[DataObjectSchema.Ranking]);
        }

        [TestMethod]
        public void TestBugsAvailable()
        {
            Assert.AreEqual(Resources.FullBug1IdChain, Context.Projects[Resources.Project1Id].Bugs[Resources.Bug1Id].IdChain);
            Assert.AreEqual(Resources.FullBug2IdChain, Context.Projects[Resources.Project1Id].Bugs[Resources.Bug2Id].IdChain);
            Assert.AreEqual(Resources.FullBug3IdChain, Context.Projects[Resources.Project1Id].Stories[Resources.UserStory4Id].Bugs[Resources.Bug3Id].IdChain);
            Assert.AreEqual(Resources.FullBug4IdChain, Context.Projects[Resources.Project1Id].Iterations[Resources.Iteration1Id].Stories[Resources.UserStory3Id].Bugs[Resources.Bug4Id].IdChain);
            Assert.AreEqual(Resources.FullBug5IdChain, Context.Projects[Resources.Project2Id].Iterations[Resources.Iteration3Id].Bugs[Resources.Bug5Id].IdChain);
        }

        [TestMethod]
        public void TestChangeBugState()
        {
            CheckStateSet(ActiveState, ResolvedState);
            CheckStateSet(ResolvedState, ClosedState);
            CheckStateSet(ClosedState, ActiveState);
            CheckStateSet(ResolvedState, ActiveState);
            CheckStateSet(ActiveState, ActiveState);
        }

        [TestMethod]
        public void TestCanChangeBugState()
        {
            Assert.IsTrue(ToTest.CanChangeState(ActiveState));
            Assert.IsFalse(ToTest.CanChangeState(ClosedState));
            Assert.IsTrue(ToTest.CanChangeState(ResolvedState));
        }

        /// <summary>
        /// Gets the default schema of the instance to test.
        /// </summary>
        /// <param name="schema">Specifies the parent schema instance.</param>
        /// <returns>Returns the retrieved schema instance.</returns>
        protected override BugSchema GetSchema(ScrumTableV1Schema schema)
        {
            return schema.BugSchema;
        }

        private void CheckStateSet(ScrumState oldState, ScrumState newState)
        {
            ToTest.State = oldState;
            Assert.AreEqual(oldState, ToTest.State);
            ToTest.State = newState;
            Assert.AreEqual(newState, ToTest.State);
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion
    }
}
